package com.bridge.applets.utils;

import com.bridge.common.exception.ServiceException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Slf4j
public class RedisLockUtil {


    private Long waitSeconds = 3L;
    private Long leaseSeconds = 3L;

    private final RedissonClient redissonClient;


    public boolean tryLock(String key, Runnable runnable) {
        return tryLock(key, waitSeconds, leaseSeconds, runnable);
    }

    public boolean tryLock(String key, Long waitSeconds, Long leaseSeconds, Runnable runnable) {
        RLock lock = redissonClient.getLock(key);
        try {
            if (lock.tryLock(waitSeconds, leaseSeconds, TimeUnit.SECONDS)) {
                runnable.run();
                return true;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            unlock(lock, key);
        }

        return false;
    }

    /**
     * 带锁执行，拿不到锁抛异常
     *
     * @param key
     * @param errorMsg
     * @param runnable
     */
    public void executeNoLockThrowException(String key, String errorMsg, Runnable runnable) {
        RLock lock = redissonClient.getLock(key);

        if (lock.tryLock()) {
            try {
                runnable.run();
            } finally {
                unlock(lock, key);
            }
        } else {
            throw new ServiceException(errorMsg);
        }
    }

    /**
     * 带锁执行，拿不到锁抛异常，拿到锁执行有返回值
     *
     * @param key
     * @param errorMsg
     * @param supplier
     * @param <T>
     * @return
     */
    public <T> T executeReturnNoLockThrowException(String key, String errorMsg, Supplier<T> supplier) {
        log.info("RedisLockUtil---executeReturnNoLockThrowException,处理枷锁，key:{}",key);
        RLock lock = redissonClient.getLock(key);

        if (lock.tryLock()) {
            try {
                log.info("RedisLockUtil---executeReturnNoLockThrowException,枷锁成功，key:{}",key);
                return supplier.get();
            } finally {
                log.info("RedisLockUtil---executeReturnNoLockThrowException,释放锁，key:{}",key);
                unlock(lock, key);
            }
        } else {
            log.info("RedisLockUtil---executeReturnNoLockThrowException,枷锁失败，key:{}",key);
            throw new ServiceException(errorMsg);
        }
    }

    private void unlock(RLock lock, String key) {
        try {
            lock.unlock();
        } catch (Exception e) {
            log.error("【{}】解锁异常", key, e);
        }
    }


}
